package algos;

import java.io.*;
import java.util.*;

import static utils.Common.*;

/*
 * Vocabulary class.
 * Please use it exactly in this order:
 * 1. Add one or more words with addWord()/addWordsFromFile().
 * 2. Build vocabulary with build().
 * 3. Use vocabulary with all other useful methods.
 * ...
 * 4. Profit!
 */
public class Vocabulary implements IVocabulary {

    public class WordSet implements IWordSet {
        private final int beginIndex;
        private final int endIndex;

        private WordSet(int beginIndex, int endIndex) {
            this.beginIndex = beginIndex;
            this.endIndex = endIndex;
        }

        public Iterator<Integer> iterator() {
            return new Iterator<Integer>() {
                private int index = beginIndex;

                public boolean hasNext() {
                    return index < endIndex;
                }

                public Integer next() {
                    return index++;
                }

                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }
    }

    private Set<String> wordsToAdd = newHashSet();
    private String[] words;


    public static boolean isWord(String s) {
        // TODO: some improvements are possible
        for (int i = 0, n = s.length(); i < n; i++) {
            char c = s.charAt(i);
            if (!Character.isLetter(c) && c != '\'') {
                return false;
            }
        }
        return true;
    }

    @Override
    public void addWord(String word) {
        if (words != null) {
            throw new IllegalStateException("vocabulary had been built, word '" + word + "' could not be added");
        }
        if (isWord(word)) {
            wordsToAdd.add(word);
        }
    }

    public void addWordsFromFile(String filename) {
        try {
            BufferedReader br = null;
            try {
                br = new BufferedReader(new FileReader(filename));
                while (true) {
                    String line = br.readLine();
                    if (line == null) break;
                    StringTokenizer tokenizer = new StringTokenizer(line);
                    while (tokenizer.hasMoreTokens()) {
                        addWord(tokenizer.nextToken());
                    }
                }
            } finally {
                br.close();
            }
        } catch (IOException e) {
        }
    }

    public void build() {
        words = new String[wordsToAdd.size()];
        int i = 0;
        for (String word : wordsToAdd) {
            words[i++] = word;
        }
        Arrays.sort(words);
        wordsToAdd = null;  // let GC collect it
    }

    // beware! can return negative value (when word isn't found)
    @Override
    public int getIndexFor(String word) {
        int ind =  Arrays.binarySearch(words, word);
        return (ind > 0 ? ind : NOT_FOUND);
    }

    @Override
    public IWordSet getWordsWithPrefix(String prefix) {
        int left = Arrays.binarySearch(words, prefix);
        if (left < 0) left = -left - 1;
        int right = Arrays.binarySearch(words, prefix + Character.MAX_VALUE);
        if (right < 0) right = -right - 1;
        return new WordSet(left, right);
    }

    @Override
    public int size() {
        return words.length;
    }

    @Override
    public String getWordAt(int index) {
        return words[index];
    }

}
